package com.scorpio.server;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.scorpio.Db_User;
import com.scorpio.api.Constants;
import com.scorpio.baen.UserInfo;
import com.scorpio.elem.BaseElem;
import com.scorpio.elem.IMTextElem;
import com.scorpio.server.base.BaseIMServer;
import com.scorpio.api.Config;
import com.scorpio.api.ServiceApi;
import com.scorpio.server.base.MessagePullProcessor;
import com.scorpio.server.inter.MessageObserver;
import com.scorpio.server.inter.TCPProtocolImpl;
import com.scorpio.utils.ClientManager;
import com.shooter.dao.DBManager;
import com.shooter.utils.PrintUtil;
import com.shooter.utils.StringUtil;
import org.apache.commons.codec.digest.DigestUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.List;

public class IMServer extends BaseIMServer implements MessageObserver {
    public IMServer(Selector mSelector) {
        super(mSelector);
    }

    //用户连接
    @Override
    public void doNewConnect(SocketChannel client, UserInfo user) {
        user.getMsgDistribute().setMessageObserver(this);
        PrintUtil.printMsg(ServiceApi.SMSG100 + client.socket().getInetAddress().getHostAddress());
    }


    /**
     * 登录连接操作
     *
     * @param client
     * @param user
     * @param reqJson 请求数据
     * @return
     */
    public boolean loagingConnect(SocketChannel client, UserInfo user, JSONObject reqJson) {
        int requestCode = reqJson.getIntValue(ServiceApi.REQUEST);
        String username = reqJson.getString("username");
        String token = reqJson.getString(UserInfo.TOKEN);
        if (StringUtil.checkStringNoNull(username) || StringUtil.checkStringNoNull(token)) {
            //查询数据库
            Db_User dbUser = new Db_User();
            if (StringUtil.checkStringNoNull(username)) dbUser.setPhone(username);
            if (StringUtil.checkStringNoNull(token)) dbUser.setToken(token);
            dbUser = (Db_User) DBManager.querySingle(dbUser);
            if (dbUser != null) {
                List<UserInfo> infos = ClientManager.getInstance().getAllClinet();
                dbUser.setToken(DigestUtils.md5Hex(dbUser.getAdd_time() + dbUser.getId() + System.currentTimeMillis()));
                //注册客户连接信息
                user.setUserId(dbUser.getId());
                user.setNetname(dbUser.getNetname());
                user.setToken(dbUser.getToken());
                user.setStart(UserInfo.START_RE);
                DBManager.insert(dbUser);
                //给客户返回信息
                JSONObject userreturn = new JSONObject();
                userreturn.put(UserInfo.NETNAME, dbUser.getNetname());
                userreturn.put(UserInfo.USER_ID, dbUser.getId());
                userreturn.put(UserInfo.TOKEN, dbUser.getToken());
                //踢出同设备用户
                for (int i = 0; i < infos.size(); i++) {
                    UserInfo ui = infos.get(i);
                    if (StringUtil.checkStringNoNull(ui.getUserId())) {
                        if (user.getUserId().equals(ui.getUserId())) {
                            if (user.getClient() != ui.getClient()) {
                                ui.clearInfo();
                            }
                        }
                    }
                }
                sendMessage(client, getServerMeg(ServiceApi.CODE_OK, requestCode, userreturn));
                PrintUtil.printMsg(user.getNetname() + " 登录");
                //登录给其他用户更新会话列表
                for (int i = 0; i < infos.size(); i++) {
                    UserInfo ui = infos.get(i);
                    if (StringUtil.checkStringNoNull(ui.getUserId())) {
                        if (!ui.getUserId().equals(user.getUserId())) {
                            JSONObject rj = new JSONObject();
                            rj.put(ServiceApi.REQUEST, Constants.ACTION_CONSTANTS);
                            handlePullConstants(ui.getClient(), ui, rj);
                        }
                    }
                }
                return false;
            }
        }
        sendMessage(client, getServerMeg(ServiceApi.CODE_ERROR, requestCode, ServiceApi.MESSAGE2));
        return false;
    }

    //用户断开
    @Override
    public void doDisConnect(SocketChannel client, UserInfo user) {
        exit(user);
        PrintUtil.printMsg(ServiceApi.SMSG101 + client.socket().getInetAddress().getHostAddress());
    }

    //收到客户消息用户消息
    @Override
    public void doNetworkMessage(SocketChannel client, UserInfo user, String msg) {
        System.out.println(msg);
        if (user != null) {
            user.addMessageBuffer(msg);
        }
    }

    /**
     * 重新分发的客户消息 经过处理后 可以的消息队列
     * 异步处理
     *
     * @param client
     * @param json
     */
    @Override
    public void doHandleMessage(SocketChannel client, UserInfo user, JSONObject json) {
        user.setLastHearBeatTile(System.currentTimeMillis());
        //检查是否注册信息客户
        if (!isRegister(client, user, json)) return;
        //获取执行动作编码
        int userAction = json.getIntValue(Constants.RS_ACTION);
        //请求码
        int requestCode = json.getIntValue(ServiceApi.REQUEST);
        switch (userAction) {
            case Constants.ACTION_TYPE_TEXT://文本消息
                handleTextMessage(client, user, json);
                break;
            case Constants.ACTION_CONSTANTS://获取会话列表
                handlePullConstants(client, user, json);
                break;
            case Constants.ACTION_EXIT:
                exit(user);
                break;
            default:
                sendMessage(client, getServerMeg(ServiceApi.CODE_ERROR, requestCode, ServiceApi.MESSAGE10));
                break;
        }

    }

    /**
     * 发送消息列表
     *
     * @param client
     * @param user
     * @param requestJson
     * @return
     */
    public boolean handlePullConstants(SocketChannel client, UserInfo user, JSONObject requestJson) {
        int requestCode = requestJson.getIntValue(ServiceApi.REQUEST);
        JSONObject returnJson = new JSONObject();
        JSONArray array = new JSONArray();
        List<UserInfo> userInfos = ClientManager.getInstance().getAllClinet();
        for (int i = 0; i < userInfos.size(); i++) {
            UserInfo u = userInfos.get(i);
            if (!StringUtil.checkStringNoNull(u.getUserId()) || u.getUserId().equals(user.getUserId())) {
                continue;
            }
            JSONObject object = new JSONObject();
            object.put(UserInfo.NETNAME, u.getNetname());
            object.put(UserInfo.USER_ID, u.getUserId());
            object.put(UserInfo.START, u.getStart());
            object.put("elem", new JSONObject().toJSONString());
            array.add(object);
        }
        returnJson.put("list", array);
        returnJson.put(ServiceApi.CODE, ServiceApi.CODE_OK);
        returnJson.put(ServiceApi.REQUEST, requestCode);
        sendMessage(client, returnJson);
        return true;
    }

    /**
     * 处理文本消息
     *
     * @param client      请求客户
     * @param user        请求用户
     * @param requestJson 请求数据
     * @return 处理成功失败
     */
    private boolean handleTextMessage(SocketChannel client, UserInfo user, JSONObject requestJson) {
        IMTextElem elem = new IMTextElem(requestJson);
        int requestCode = requestJson.getIntValue(ServiceApi.REQUEST);
        int userAction = requestJson.getIntValue(Constants.RS_ACTION);
        if (!StringUtil.checkStringNoNull(elem.getContext())) {
            sendMessage(client, getServerMeg(ServiceApi.CODE_ERROR, requestCode, ServiceApi.MESSAGE11));
            return false;
        } else if (!StringUtil.checkStringNoNull(elem.getAcceptorId())) {
            sendMessage(client, getServerMeg(ServiceApi.CODE_ERROR, requestCode, ServiceApi.MESSAGE9));
            return false;
        }
        //获取发送给的客户
        UserInfo onLineUser = ClientManager.getInstance().inUsernameGetInfo(elem.getAcceptorId());
        //用户不在线的时候
        if (onLineUser == null) {
            requestJson.put(ServiceApi.CODE, ServiceApi.CODE_ERROR);
            requestJson.put(ServiceApi.MSG, "用户不在线");
            //给用户自己也发一个消息。表示发送成功
            sendMessage(user.getClient(), requestJson);
            return false;
        }
        elem.setSenderId(user.getUserId());
        JSONObject returnJson = elem.toMessageJson();
        returnJson.put(ServiceApi.CODE, ServiceApi.CODE_OK);
        returnJson.put(ServiceApi.REQUEST, requestCode);
        returnJson.put(Constants.RS_ACTION, userAction);
        if (sendMessage(onLineUser.getClient(), returnJson)) {
            //给用户自己也发一个消息。表示发送成功
            sendMessage(user.getClient(), returnJson);
        }
        return true;
    }

    /**
     * 检查用户是否登录
     *
     * @param client
     * @param user
     * @param json
     * @return
     */
    private boolean isRegister(SocketChannel client, UserInfo user, JSONObject json) {
        int userAction = json.getIntValue(Constants.RS_ACTION);
        int requestCode = json.getIntValue(ServiceApi.REQUEST);
        if (userAction == Constants.ACTION_LOGING) {//登录操作
            return loagingConnect(client, user, json);
        } else if (userAction == Constants.ACTION_HEART_BEAT1) {//心跳请求回
            int hb = json.getIntValue(Constants.HB);
            long timg = json.getLongValue(Constants.EL_TIME);
            if (hb == Constants.HB_REQUEST) {
                //客户的心跳请求
                sendMessage(client, user.getHeartBeatResult(timg));
            } else if (hb == Constants.HB_RESULT) {
                //服务器的心跳请求回执结果（到此表示心跳成功）
//                    PrintUtil.printMsg("心跳请求时间=" + (System.currentTimeMillis() / 1000 - timg));
            }
            return false;
        } else {
            if (!StringUtil.checkStringNoNull(user.getUserId())) {
                sendMessage(client, getServerMeg(ServiceApi.CODE_PAST_INFO, requestCode, ServiceApi.MESSAGE12));
                return false;
            }
        }
        return true;
    }

    /**
     * 退出操作
     */
    private void exit(UserInfo user) {
        JSONObject json = new JSONObject();
        json.put(Constants.RS_ACTION, Constants.ACTION_UPDATE_START);
        json.put(UserInfo.USER_ID, user.getUserId());
        json.put(UserInfo.START, UserInfo.START_US);
        giveAllUserSendMessage(user, json);
        user.clearInfo();
    }

    /**
     * 给所有用户发送一个消息
     *
     * @param user        除此以外的用户 不传所有
     * @param requestJson
     */
    private void giveAllUserSendMessage(UserInfo user, JSONObject requestJson) {
        List<UserInfo> infos = ClientManager.getInstance().getAllClinet();
        for (int i = 0; i < infos.size(); i++) {
            UserInfo ui = infos.get(i);
            if (StringUtil.checkStringNoNull(ui.getUserId())) {
                if (user != null && ui.getUserId().equals(user.getUserId())) {
                    continue;
                }
                sendMessage(ui.getClient(), requestJson);
            }
        }
    }

    private boolean sendMessage(SocketChannel client, JSONObject json) {
        try {
            synchronized (client) {
                String returnMsg = json.toJSONString() + MessagePullProcessor.MSG_END;
                client.write(TCPProtocolImpl.charset.encode(returnMsg));
            }
            return true;
        } catch (IOException e) {
        }
        return false;
    }

    //发送消息
    @Override
    public String doSendMessage(SocketChannel client, UserInfo user) {
        //心跳任务
        long last_time = user.getLastHearBeatTile();
        if (System.currentTimeMillis() - last_time > Config.BEART_BEAT_PERIOD) {
            user.setLastHearBeatTile(System.currentTimeMillis());
            return user.getHeartBeatRequest().toJSONString();
        }
        return null;
    }

    //启动服务
    public static void openServer() {
        //打开socket监听
        try {
            // 创建选择器
            Selector selector = Selector.open();
            // 打开监听信道
            ServerSocketChannel listenerChannel = ServerSocketChannel.open();
            // 与本地端口绑定
            listenerChannel.socket().bind(new InetSocketAddress(Config.IM_PORT));
            // 设置为非阻塞模式
            listenerChannel.configureBlocking(false);
            // 将选择器绑定到监听信道,只有非阻塞信道才可以注册选择器.并在注册过程中指出该信道可以进行Accept操作
            listenerChannel.register(selector, SelectionKey.OP_ACCEPT);
            new Thread(new IMServer(selector)).start();
            PrintUtil.printMsg(ServiceApi.SMSG5);
        } catch (IOException e) {
            e.printStackTrace();
            PrintUtil.printMsg(ServiceApi.SMSG6);
        }
    }

}